// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: daniel

#include "coin2/exchange/base/order/rate_config.h"

#include <fstream>
#include <memory>
#include <string>

#include "coin2/base/string_util.h"
#include "coin2/exchange/base/market/enums.h"
#include "coin2/exchange/base/market/market_exchange_api.h"
#include "coin2/exchange/order/connection.pb.h"

namespace coin2::exchange::base::order {

const std::string RateConfig::cfg_path_ = "data/coin2/order/og_req_rate.json";

auto RateConfig::Get() -> std::unique_ptr<RateConfig> {
  std::unique_ptr<RateConfig> rate_config{new RateConfig(cfg_path_)};
  return rate_config;
}

void RateConfig::CheckFail(std::string_view mea_str, std::string_view example) {
  CHECK(false) << "Imcomplete request rate config found for mea: " << mea_str << ".\n"
               << "Please check default configuration file : " << cfg_path_ << ".\n"
               << "Or you can configure customized rate in driver config. For example: " << example;
}

auto RateConfig::GetMeStringFromMea(const MarketExchangeApi& mea) -> std::string {
  using coin2::exchange::base::market::ExchangeTypeToString;
  using coin2::exchange::base::market::MarketTypeToString;
  std::string me =
      fmt::format("{}_{}", MarketTypeToString(mea.market), ExchangeTypeToString(mea.exchange));
  StringToLowerInPlace(&me);
  return me;
}

bool RateConfig::LoadConfigFromJson(const nlohmann::json& json, google::protobuf::Message* config) {
  auto status = google::protobuf::util::JsonStringToMessage(json, config);
  if (!status.ok()) {
    LOG(ERROR) << "failed to read rate limit info from configuration, error="
               << status;
    return false;
  }
  return true;
}

void RateConfig::MergeFromConfig(
    const std::string field_name,
    const google::protobuf::Message& src_cfg,
    google::protobuf::Message* dst_cfg) {
  auto field_mea = src_cfg.GetDescriptor()->FindFieldByName(field_name);
  if (!field_mea) {
    return;
  }
  dst_cfg->MergeFrom(src_cfg.GetReflection()->GetMessage(src_cfg, field_mea));
}

RateConfig::RateConfig(std::string_view cfg_file) {
  std::ifstream ifs(cfg_file.data());
  config_ = nlohmann::json::parse(ifs);
}

void RateConfig::LoadDefaultConfig(const std::string& mea_str, google::protobuf::Message* config)
    const {
  if (!config_.contains(mea_str)) {
    return;
  }
  LoadConfigFromJson(config_[mea_str].dump(), config);
}

void RateConfig::GetConfig(
    const MarketExchangeApi& mea,
    const coin2::exchange::order::OrderConnectionConfig& cfg,
    google::protobuf::Message* config) const {
  // 1.load default configuration
  LoadDefaultConfig(mea.String(), config);
  // 2.merge customized configuration
  if (!cfg.has_rate_limit_info()) {
    return;
  }
  MergeFromConfig(GetMeStringFromMea(mea), cfg.rate_limit_info(), config);
}

}  // namespace coin2::exchange::base::order
